﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using ICeFramework.DataManagement.Interfaces;
using ICeFramework.Utilities.IO;
using ICeFramework.Utilities.Packaging;
using ICeFramework.Win.UI;
using SharpGen.Templates;
using SharpGen.Common;
using SharpGen.Enums;
using SharpGen.Common.Enums;
using ICeFramework.DataAccess.Interfaces;
using SharpGen.Forms;
using System.Linq;
using ICeFramework.Utilities.VisualStudio;
using Plugin.Designer;
using SharpGen.TemplateInterface;
using Plugin;

namespace SharpGen
{
    public partial class MainForm : Form
    {
        #region Main

        private const string _title = "Code Gen";
        private string _projectFile;
        FindReplaceForm _findReplaceForm;

        #region public string ProjectFile
        /// <summary>
        /// Get/Sets the ProjectFile of the MainForm
        /// </summary>
        /// <value></value>
        public string ProjectFile
        {
            get { return _projectFile; }
            set
            {
                if (_project != null)
                {
                    _project.Dispose();
                    _project = null;
                }
                _projectFile = value;
                InitProject(value);
            }
        }
        #endregion

        private string _debugFile;
        #region public string DebugFile
        /// <summary>
        /// Get/Sets the DebugFile of the MainForm
        /// </summary>
        /// <value></value>
        public string DebugFile
        {
            get { return _debugFile; }
            set { _debugFile = value; }
        }
        #endregion

        private static MainForm _instance = null;
        #region public static MainForm Instance
        /// <summary>
        /// Gets the Instance of the MainForm
        /// </summary>
        /// <value></value>
        public static MainForm Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new MainForm();
                }
                return _instance;
            }
        }
        #endregion

        #region public string OutputPath
        /// <summary>
        /// Get/Sets the OutputPath of the MainForm
        /// </summary>
        /// <value></value>
        public string OutputPath
        {
            get { return Properties.Settings.Default.OutputPath; }
            set
            {
                txtSavePath.Text = value;
                Properties.Settings.Default.OutputPath = value;
                Properties.Settings.Default.Save();
            }
        }
        #endregion

        #region public string OutputNamespace
        /// <summary>
        /// Get/Sets the OutputNamespace of the MainForm
        /// </summary>
        /// <value></value>
        public string OutputNamespace
        {
            get { return Properties.Settings.Default.OutputNamespace; }
            set
            {
                txtNamespace.Text = value;
                Properties.Settings.Default.OutputNamespace = value;
                Properties.Settings.Default.Save();
            }
        }
        #endregion

        #region public MainForm()
        /// <summary>
        /// Initializes a new instance of the <b>MainForm</b> class.
        /// </summary>
        public MainForm()
        {
            InitializeComponent();
        }
        #endregion

        #region private void MainForm_Load(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the MainForm's Load event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void MainForm_Load(object sender, EventArgs e)
        {
            SetConnection(true);
            statusLabel1.Text = "Drag items from SQL Object Explorer";
            selectedObjectTree.ImageList = dbTree.ImageList;
            _project.ProjectChanged += new EventHandler(_project_ProjectChanged);
            _project.ProjectSaved += new EventHandler(_project_ProjectSaved);

            string outPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "OutputCompilation");
            IOUtil.DeleteDirectoryContents(outPath);
        }
        #endregion

        #region void _project_ProjectSaved(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the _project's ProjectSaved event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        void _project_ProjectSaved(object sender, EventArgs e)
        {
            string debugText = string.IsNullOrEmpty(_debugFile) ? string.Empty : string.Format(" - Debug ({0})", _debugFile);
            this.Text = string.Format("{0} - [{1}]{2}", _title, _project.Name, debugText);
        }
        #endregion

        #region void _project_ProjectChanged(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the _project's ProjectChanged event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        void _project_ProjectChanged(object sender, EventArgs e)
        {
            string debugText = string.IsNullOrEmpty(_debugFile) ? string.Empty : string.Format(" - Debug ({0})", _debugFile);
            this.Text = string.Format("{0} - [{1}]{2}{3}", _title, _project.Name, _project.IsModified ? "*" : "", debugText);
        }
        #endregion

        #region private void InitProject(string projectFile)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="projectFile"></param>
        private void InitProject(string projectFile)
        {
            string error = string.Empty;
            _project = (SharpGenProject)PluginHost<ICodeGenTemplate, ICodeGenHost>.GetProject(projectFile, out error);
            if (_project == null)
            {
                if (!string.IsNullOrEmpty(error))
                {
                    MessageBox.Show(error);
                }
                _project = new SharpGenProject(string.Empty);
                _project.GetFilename += new GetFilenameEventHandler(_project_GetFilename);
                _project.Message += new MessageEventHandler(_project_Message);
            }
            string debugText = string.IsNullOrEmpty(_debugFile) ? string.Empty : string.Format(" - Debug ({0})", _debugFile);
            this.Text = string.Format("{0} - [{1}]{2}{3}", _title, _project.IsNew ? "New Project" : _project.Name, _project.Name, _project.IsModified ? "*" : "", debugText);
            RefreshSelectedObjectsDataFromProject();
        }
        #endregion

        void _project_Message(object sender, MessageEventArgs e)
        {
            bool canShow = (e.MessageType == MessageType.Internal && Debugger.IsAttached) | e.MessageType != MessageType.Internal;
            if (!canShow)
            {
                return;
            }

            switch (e.MessageType)
            {
                case MessageType.Error:
                    MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    break;
                case MessageType.Info:
                    MessageBox.Show(e.Message, "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    break;
                case MessageType.Internal:
                    MessageBox.Show(e.Message, "Internal Message", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    break;
                case MessageType.Warn:
                    MessageBox.Show(e.Message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    break;
                default:
                    break;
            }
        }

        #region private void MainForm_KeyDown(object sender, KeyEventArgs e)
        /// <summary>
        /// This method is called when the MainForm's KeyDown event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="KeyEventArgs"/> of the event.</param>
        private void MainForm_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.F5)
            {
                //GenerateOutput();
            }
        }
        #endregion

        #endregion

        #region Data Collector
        private ISqlConnection _connection;
        private string editNodeStaticText = string.Empty;

        #region public string SelectedDatabase
        /// <summary>
        /// Gets the SelectedDatabase of the MainForm
        /// </summary>
        /// <value></value>
        public string SelectedDatabase
        {
            get { return _connection != null ? _connection.Database : string.Empty; }
        }
        #endregion

        #region public SqlObjectCollection SelectedObjects
        /// <summary>
        /// Gets the SelectedObjects of the MainForm
        /// </summary>
        /// <value></value>
        public SqlObjectCollection SelectedObjects
        {
            get { return GetSelectedObjects(); }
        }
        #endregion

        private string _classNamePrefix;
        #region public string ClassNamePrefix
        /// <summary>
        /// Get/Sets the ClassNamePrefix of the MainForm
        /// </summary>
        /// <value></value>
        public string ClassNamePrefix
        {
            get { return _classNamePrefix; }
            set { _classNamePrefix = value; }
        }
        #endregion

        private string _classNameSuffix;
        #region public string ClassNameSuffix
        /// <summary>
        /// Get/Sets the ClassNameSuffix of the MainForm
        /// </summary>
        /// <value></value>
        public string ClassNameSuffix
        {
            get { return _classNameSuffix; }
            set { _classNameSuffix = value; }
        }
        #endregion

        #region private SqlObjectCollection GetSelectedObjects()
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private SqlObjectCollection GetSelectedObjects()
        {
            SqlObjectCollection selectedObjects = new SqlObjectCollection();
            foreach (TreeNode tn in selectedObjectTree.Nodes)
            {
                SqlObject obj = tn.Tag as SqlObject;
                if (obj != null)
                {
                    selectedObjects.Add(obj);
                }
            }
            return selectedObjects;
        }
        #endregion

        #region private void SetConnection(bool autoConnect)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="autoConnect"></param>
        private void SetConnection(bool autoConnect)
        {
            SqlConnectionDialog dlg = new SqlConnectionDialog("SQL Manager");
            try
            {
                if (dlg.GetDialog(autoConnect))
                {
                    lnkConnectionString.Text = string.Format("Connection String: {0}", dlg.ConnectionString);
                    _connection = dlg.Connection;
                }
            }
            catch
            {
                if (dlg.GetDialog(false))
                {
                    lnkConnectionString.Text = string.Format("Connection String: {0}", dlg.ConnectionString);
                    _connection = dlg.Connection;
                }
            }
            if (_connection == null)
            {
                Environment.Exit(0);
            }
            else
            {
                dbTree.ConnectionString = _connection.ConnectionString;
                dbTree.RefreshServer();
            }
        }
        #endregion

        #region private void lnkConnectionString_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        /// <summary>
        /// This method is called when the lnkConnectionString's LinkClicked event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="LinkLabelLinkClickedEventArgs"/> of the event.</param>
        private void lnkConnectionString_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            SetConnection(false);
        }
        #endregion

        #region private void btnSavePath_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the btnSavePath's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void btnSavePath_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                txtSavePath.Text = folderBrowserDialog1.SelectedPath;
            }
        }
        #endregion

        #region private void dbTree_ItemDrag(object sender, ItemDragEventArgs e)
        /// <summary>
        /// This method is called when the dbTree's ItemDrag event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="ItemDragEventArgs"/> of the event.</param>
        private void dbTree_ItemDrag(object sender, ItemDragEventArgs e)
        {
            DoDragDrop(e.Item, DragDropEffects.Move);
        }
        #endregion

        #region private void selectedObjectTree_DragEnter(object sender, DragEventArgs e)
        /// <summary>
        /// This method is called when the selectedObjectTree's DragEnter event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="DragEventArgs"/> of the event.</param>
        private void selectedObjectTree_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false))
            {
                TreeNode sourceNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
                if (sourceNode.Tag == null || sourceNode.Tag.GetType() != typeof(DBObject))
                {
                    e.Effect = DragDropEffects.None;
                    return;
                }
                else
                {
                    switch (((DBObject)sourceNode.Tag).ObjectType)
                    {
                        case NodeType.Table:
                        case NodeType.View:
                            e.Effect = DragDropEffects.Move;
                            break;
                        default:
                            e.Effect = DragDropEffects.None;
                            break;
                    }
                }
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }
        #endregion

        #region private void selectedObjectTree_DragDrop(object sender, DragEventArgs e)
        /// <summary>
        /// This method is called when the selectedObjectTree's DragDrop event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="DragEventArgs"/> of the event.</param>
        private void selectedObjectTree_DragDrop(object sender, DragEventArgs e)
        {
            TreeNode sourceNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
            AddSelected(sourceNode, false);
            RefreshSelectedObjectsDataFromProject();
            statusLabel1.Text = "Drag items from SQL Object Explorer";
        }
        #endregion

        #region private SqlObject AddSelected(TreeNode node, bool isRelation)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        /// <param name="isRelation"></param>
        /// <returns></returns>
        private SqlObject AddSelected(TreeNode node, bool isRelation)
        {
            if (selectedObjectTree.Nodes.ContainsKey(node.Name))
            {
                return null;
            }
            if (node.Tag == null || (node.Tag != null && node.Tag.GetType() != typeof(DBObject)))
            {
                return null;
            }
            TreeNode destNode = (TreeNode)node.Clone();
            destNode.Nodes.Clear();

            DBObject dbObj = (DBObject)node.Tag;

            NodeType objectType = dbObj.ObjectType;

            TreeNode ndObjectType = new TreeNode("Type: ");

            string objectName = destNode.Text.EndsWith(" (expanding...)") ? destNode.Text.Substring(0, destNode.Text.Length - 15) : destNode.Text;

            SqlObject obj = new SqlObject(string.Format("{0} ({1})", objectName, objectType.ToString()));
            statusLabel1.Text = string.Format("Adding{0} {1} {2}...", isRelation ? " related" : "", objectType.ToString().ToLower(), objectName);
            Application.DoEvents();
            switch (objectType)
            {
                case NodeType.Table:
                    ndObjectType.Text += " Table";
                    obj.ObjectType = SqlObjectType.Table;
                    ITable tbl = dbObj.UnderlayingObject as ITable;
                    tbl.GetColumns();
                    tbl.GetForeignKeys();
                    if (tbl.ForeignKeys.Count > 0)
                    {
                        //TreeNode fkHolder = new TreeNode("Related Entities");
                        List<string> propertyNames = new List<string>();
                        foreach (IForeignKey fk in tbl.ForeignKeys)
                        {
                            SqlObject fkObj = AddRelatedEntity(fk);
                            if (fkObj != null)
                            {
                                SqlRelation relation = new SqlRelation(fk.ReferencedTable, SqlRelationType.OneToMany);
                                foreach (IForeignKeyColumn fkCol in fk.Columns)
                                {
                                    relation.Columns.Add(fkCol.Name, fkCol.ReferencedColumn);
                                    if (string.IsNullOrEmpty(relation.PropertyName) && !propertyNames.Contains(fkCol.Name))
                                    {
                                        relation.PropertyName = GetRelationPropertyName(fkCol.Name);
                                        propertyNames.Add(relation.PropertyName);
                                    }
                                }
                                if (string.IsNullOrEmpty(relation.PropertyName))
                                {
                                    relation.PropertyName = fk.Name;
                                }
                                obj.RelatedEntities.Add(relation);
                            }
                        }
                        //destNode.Nodes.Add(fkHolder);
                    }
                    dbObj.UnderlayingObject = tbl;
                    break;
                case NodeType.View:
                    ndObjectType.Text += " View";
                    IView view = dbObj.UnderlayingObject as IView;
                    view.GetColumns();
                    dbObj.UnderlayingObject = view;
                    obj.ObjectType = SqlObjectType.View;
                    break;
                case NodeType.SP:
                    ndObjectType.Text += " Stored Procedure";
                    obj.ObjectType = SqlObjectType.StoredProcedure;
                    break;
                default:
                    return null;
            }
            obj.Data = dbObj.UnderlayingObject;
            //objectName = string.Format("{0}{1}{2}", _project.ClassNamePrefix, objectName, _project.ClassNameSuffix);
            //string className = string.Format("{0}{1}{2}", _project.ClassNamePrefix, objectName, _project.ClassNameSuffix);
            //string className = string.Format("{0}{1}{2}", _project.ClassNamePrefix, objectName, _project.ClassNameSuffix);
            obj.ClassName = objectName;
            obj.FileName = string.Format("{0}.cs", objectName);
            destNode.Nodes.Add(ndObjectType);
            destNode.Nodes.Add(new TreeNode(string.Format("Class Name: {0}", obj.ClassName)));
            destNode.Nodes.Add(new TreeNode(string.Format("File Name: {0}", obj.FileName)));
            destNode.Tag = obj;
            destNode.Text = objectName;
            selectedObjectTree.Nodes.Add(destNode);
            //_selectedObjects.Add(obj);
            return obj;
        }
        #endregion

        #region private SqlObject AddRelatedEntity(IForeignKey fk)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fk"></param>
        /// <returns></returns>
        private SqlObject AddRelatedEntity(IForeignKey fk)
        {
            if (fk == null)
            {
                return null;
            }

            if (fk.Parent.Name == fk.ReferencedTable)
            {
                return null;
            }

            TreeNode refTblNode = dbTree.GetNodeByName(fk.ReferencedTable, NodeType.Table);
            if (refTblNode == null)
            {
                return null;
            }
            return AddSelected(refTblNode, true);
        }
        #endregion

        #region private string GetRelationPropertyName(string columnName)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns></returns>
        private string GetRelationPropertyName(string columnName)
        {
            string col = columnName;
            if (col.StartsWith("ID_"))
            {
                return col.Substring(3);
            }
            if (col.StartsWith("ID"))
            {
                return col.Substring(2);
            }
            if (col.EndsWith("ID"))
            {
                return col.Substring(0, col.Length - 2);
            }
            if (col.EndsWith("_ID"))
            {
                return col.Substring(0, col.Length - 3);
            }
            return "Related_" + columnName;
        }
        #endregion

        #region private void dbTree_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        /// <summary>
        /// This method is called when the dbTree's NodeMouseDoubleClick event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="TreeNodeMouseClickEventArgs"/> of the event.</param>
        private void dbTree_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Button != MouseButtons.Left)
            {
                return;
            }
            AddSelected(e.Node, false);
            RefreshSelectedObjectsDataFromProject();
        }
        #endregion

        #region private void selectedObjectTree_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
        /// <summary>
        /// This method is called when the selectedObjectTree's BeforeLabelEdit event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="NodeLabelEditEventArgs"/> of the event.</param>
        private void selectedObjectTree_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            string staticText = string.Empty;
            if (e.Node.Text.StartsWith("Class Name: "))
            {
                staticText = "Class Name: ";
            }
            if (e.Node.Text.StartsWith("File Name: "))
            {
                staticText = "File Name: ";
            }
            if (string.IsNullOrEmpty(staticText))
            {
                e.CancelEdit = true;
            }
            else
            {
                e.Node.Text = e.Node.Text.Substring(staticText.Length);
                editNodeStaticText = staticText;
            }
        }
        #endregion

        #region private void selectedObjectTree_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
        /// <summary>
        /// This method is called when the selectedObjectTree's AfterLabelEdit event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="NodeLabelEditEventArgs"/> of the event.</param>
        private void selectedObjectTree_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            if (!string.IsNullOrEmpty(editNodeStaticText))
            {
                e.Node.Text = editNodeStaticText + e.Label;
                editNodeStaticText = string.Empty;
            }
            else
            {
                e.Node.Text = e.Label;
            }
            StoreModifications(e.Node);
        }
        #endregion

        #region private void StoreModifications(TreeNode node)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        private void StoreModifications(TreeNode node)
        {
            if (node == null)
            {
                return;
            }
            TreeNode tnParent = node.Parent;
            if (tnParent == null || (tnParent != null && tnParent.Tag as SqlObject == null))
            {
                return;
            }
            SqlObject obj = tnParent.Tag as SqlObject;
            if (node.Text.StartsWith("Class Name: "))
            {
                obj.ClassName = node.Text.Substring(11);
            }
            if (node.Text.StartsWith("File Name: "))
            {
                obj.FileName = obj.FileName = Path.Combine(OutputPath, node.Text.Substring(10));
            }
            node.Parent.Tag = obj;
        }
        #endregion

        #region private void RefreshSelectedObjectsDataFromProject()
        /// <summary>
        /// 
        /// </summary>
        private void RefreshSelectedObjectsDataFromProject()
        {
            foreach (TreeNode tn in selectedObjectTree.Nodes)
            {
                SqlObject obj = tn.Tag as SqlObject;
                obj.ClassName = _project.StringReplacement.ReplaceText(tn.Text, StringReplacementType.ClassName, obj.ObjectType);

                obj.FileName = string.Format("{0}.cs", _project.StringReplacement.ReplaceText(tn.Text, StringReplacementType.FileName, obj.ObjectType));

                tn.Tag = obj;
                foreach (TreeNode tnChild in tn.Nodes)
                {
                    if (tnChild.Text.StartsWith("Class Name"))
                    {
                        tnChild.Text = string.Format("Class Name: {0}", obj.ClassName);
                    }
                    else if (tnChild.Text.StartsWith("File Name"))
                    {
                        tnChild.Text = string.Format("File Name: {0}", obj.FileName);
                    }
                }
            }
        }
        #endregion

        #region private void selectedObjectTree_ValidateLabelEdit(object sender, ValidateLabelEditEventArgs e)
        /// <summary>
        /// This method is called when the selectedObjectTree's ValidateLabelEdit event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="ValidateLabelEditEventArgs"/> of the event.</param>
        private void selectedObjectTree_ValidateLabelEdit(object sender, ValidateLabelEditEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Label))
            {
                MessageBox.Show(string.IsNullOrEmpty(editNodeStaticText) ? "Text cannot be empty" : string.Format("{0} cannot be empty !", editNodeStaticText), "Selected Object", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                e.Cancel = true;
            }
        }
        #endregion

        #region private void selectedObjectsMenu_Opening(object sender, CancelEventArgs e)
        /// <summary>
        /// This method is called when the selectedObjectsMenu's Opening event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="CancelEventArgs"/> of the event.</param>
        private void selectedObjectsMenu_Opening(object sender, CancelEventArgs e)
        {
            if (selectedObjectTree.SelectedNode == null)
            {
                e.Cancel = true;
                return;
            }
            if (selectedObjectTree.SelectedNode.Parent != null)
            {
                e.Cancel = true;
                return;
            }
        }
        #endregion

        #region private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the deleteToolStripMenuItem's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (selectedObjectTree.SelectedNode == null)
            {
                return;
            }
            selectedObjectTree.SelectedNode.Remove();
        }
        #endregion

        #region private void txtClassNamePrefix_TextChanged(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the txtClassNamePrefix's TextChanged event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void txtClassNamePrefix_TextChanged(object sender, EventArgs e)
        {
            //_project.ClassNamePrefix = txtClassNamePrefix.Text;
            RefreshSelectedObjectsDataFromProject();
        }
        #endregion

        #region private void txtClassNameSuffix_TextChanged(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the txtClassNameSuffix's TextChanged event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void txtClassNameSuffix_TextChanged(object sender, EventArgs e)
        {
            //_project.ClassNameSuffix = txtClassNameSuffix.Text;
            RefreshSelectedObjectsDataFromProject();
        }
        #endregion

        #endregion

        #region private void btnGenerate_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the btnGenerate's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void btnGenerate_Click(object sender, EventArgs e)
        {
            GenerateOutput();
        }
        #endregion

        #region public void GenerateOutput()
        /// <summary>
        /// 
        /// </summary>
        public void GenerateOutput()
        {
            string assemblyFile = string.IsNullOrEmpty(_debugFile) ? _project.PrimaryAssembly : _debugFile;
            if (string.IsNullOrEmpty(assemblyFile))
            {
                MessageBox.Show("There is not template associated with this project.", "Class generation abort", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            if (string.IsNullOrEmpty(OutputPath))
            {
                MessageBox.Show("You must first select a valid output path !", "Class generation abort", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            if (string.IsNullOrEmpty(OutputNamespace))
            {
                MessageBox.Show("You must first select an output namespace.", "Class generation abort", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            tabControl1.SelectedTab = tabControl1.TabPages[1];
            logTextBox.Clear();


            string outPath = Path.Combine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "OutputCompilation"), Path.GetRandomFileName());
            Directory.CreateDirectory(outPath);

            string outFileName = assemblyFile;
            if (string.IsNullOrEmpty(_debugFile))
            {
                IOUtil.DeleteDirectoryContents(outPath);
                ZipUtil.UnZipFiles(_project.FileName, outPath, string.Empty, false);
                outFileName = Path.Combine(outPath, string.Format("{0}.{1}.{2}", Path.GetFileNameWithoutExtension(_project.PrimaryAssembly), Path.GetRandomFileName(), Path.GetExtension(_project.PrimaryAssembly)));
                File.Move(Path.Combine(outPath, _project.PrimaryAssembly), outFileName);
            }

            if (!Global.Global.Templates.AssemblyLoaded(outFileName))
            {
                Global.Global.Templates.AddPlugin(outFileName, true);
            }

            foreach (PluginReference<ICodeGenTemplate, ICodeGenHost> template in Global.Global.Templates.PluginReferenceList)
            {
                logTextBox.AppendText(string.Format("Executing template {0}\r\n", template.Instance.Name));
                Global.Global.Templates.InitializePlugin(template);

                string path = string.Empty;
                foreach (SqlObject obj in this.SelectedObjects)
                {
                    path = Path.Combine(OutputPath,
                        string.Format("{0}.generated{1}",
                        Path.GetFileNameWithoutExtension(obj.FileName),
                        Path.GetExtension(obj.FileName)));
                    logTextBox.AppendText(string.Format("Generating file {0}.\r\n", Path.GetFileName(path)));
                    if (!File.Exists(path) || (File.Exists(path) &&
                        MessageBox.Show(string.Format("The file {0} already exists. Would you like to overwrite it ?", Path.GetFileName(path)), "File already exists", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes))
                    {
                        //Global.Global.Templates.OnSqlObject(template, obj, path, true);
                    }

                    path = Path.Combine(OutputPath, obj.FileName);
                    logTextBox.AppendText(string.Format("Generating file {0}.\r\n", Path.GetFileName(path)));
                    if (!File.Exists(path) || (File.Exists(path) &&
                        MessageBox.Show(string.Format("The file {0} already exists. Would you like to overwrite it ?", obj.FileName), "File already exists", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes))
                    {
                        //Global.Global.Templates.OnSqlObject(template, obj, path, false);
                    }
                }
            }
        }
        #endregion

        #region Template Handler

        private SharpGenProject _project = null;
        #region public TemplateProject Project
        /// <summary>
        /// Get/Sets the Project of the MainForm
        /// </summary>
        /// <value></value>
        public SharpGenProject Project
        {
            get { return _project; }
            set { _project = value; }
        }
        #endregion

        #region private void btnEditEdmplate_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the btnEditEdmplate's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void btnEditEdmplate_Click(object sender, EventArgs e)
        {
            //TemplateEditDlg templateEditor = new TemplateEditDlg(_project);
            //templateEditor.FormClosing += new System.Windows.Forms.FormClosingEventHandler(templateEditor_FormClosing);
            //templateEditor.ShowDialog();
        }
        #endregion

        #region private void btnNewTemplate_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the btnNewTemplate's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void btnNewTemplate_Click(object sender, EventArgs e)
        {
            //TemplateEditDlg templateEditor = new TemplateEditDlg(null);
            //templateEditor.FormClosing += new System.Windows.Forms.FormClosingEventHandler(templateEditor_FormClosing);
            //templateEditor.ShowDialog();
        }
        #endregion

        #region private void btnBrowseTemplate_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the btnBrowseTemplate's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void btnBrowseTemplate_Click(object sender, EventArgs e)
        {
            if (CloseProject() && openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    _projectFile = openFileDialog1.FileName;
                    InitProject(_projectFile);
                    //_project = (CodeGenProject)XmlSerialization.FromXML(File.ReadAllText(openFileDialog1.FileName), typeof(CodeGenProject));
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error", ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }
        #endregion

        #region private void optionToolStripMenuItem_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the optionToolStripMenuItem's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void optionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OptionsForm optFrm = new OptionsForm();
            optFrm.ShowDialog();
        }
        #endregion

        #endregion

        #region private void openTemplateToolStripMenuItem_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the openTemplateToolStripMenuItem's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void openTemplateToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenEditor();
        }
        #endregion

        private void OpenEditor()
        {
            //if (!File.Exists(Properties.Settings.Default.VS_IDEPath))
            //{
            //    MessageBox.Show("The selected Visual Studio Version is not installed !");
            //    return;
            //}
            _project.Edit();
            //string projectPath = TemplateLocationChooser.GetProjectPath(_project.SourceProjectLocation);
            //if (!File.Exists(projectPath))
            //{
            //    return;
            //}

            //if (ModifyProjectDebugData(projectPath))
            //{
            //    _project.SourceProjectLocation = projectPath;
            //    ProcessStartInfo psi = new ProcessStartInfo(Properties.Settings.Default.VS_IDEPath);

            //    psi.Arguments = string.Format("\"{0}\"", projectPath);
            //    Process.Start(psi);
            //    Environment.Exit(0);
            //}
            //else
            //{
            //    MessageBox.Show("Cannot modify project debug data !");
            //}
        }

        #region private void ModifyProjectDebugData(string projectPath)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="projectPath"></param>
        private bool ModifyProjectDebugData(string projectPath)
        {
            try
            {
                List<string> targetList = VisualStudioIDE.GetProjectTargets(projectPath);
                string userFile = string.Format("{0}.user", projectPath);
                if (!File.Exists(userFile))
                {
                    return false;
                }
                string contents = string.Empty;
                using (TextReader tr = new StreamReader(userFile))
                {
                    contents = tr.ReadToEnd();
                }

                contents = ReplaceMiddleString(contents, "<StartProgram>", "</StartProgram>", Application.ExecutablePath);
                string args = string.Format("/debug \"{0}\\bin\\Debug\\CodeGenProjectTemplate.dll\"", Path.GetDirectoryName(projectPath));
                if (File.Exists(_project.FileName))
                {
                    args = string.Format("/t \"{0}\" /debug \"{1}\\bin\\Debug\\CodeGenProjectTemplate.dll\"", _project.FileName, Path.GetDirectoryName(projectPath));
                }
                contents = ReplaceMiddleString(contents, "<StartArguments>", "</StartArguments>", args);
                using (TextWriter tw = new StreamWriter(userFile))
                {
                    tw.Write(contents);
                }
                return true;
            }
            catch { }
            return false;
        }
        #endregion

        #region private string ReplaceMiddleString(string text, string left, string right, string replacement)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="text"></param>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <param name="replacement"></param>
        /// <returns></returns>
        private string ReplaceMiddleString(string text, string left, string right, string replacement)
        {
            int i = text.IndexOf(left);
            int j = text.IndexOf(right);
            if (i != -1 && j != -1)
            {
                return text.Substring(0, i + left.Length) + replacement + text.Substring(j, text.Length - j);
            }
            return text;
        }
        #endregion

        #region private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the saveToolStripMenuItem's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Save();
        }
        #endregion

        #region private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the saveAsToolStripMenuItem's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                SaveAs(saveFileDialog1.FileName);
            }
        }
        #endregion

        string _project_GetFilename(object sender, CancelEventArgs e)
        {
            string fileName = string.Empty;
            bool bRes = saveFileDialog1.ShowDialog() == DialogResult.OK;
            if (bRes)
            {
                fileName = saveFileDialog1.FileName;
            }
            else
            {
                e.Cancel = true;
            }
            return fileName;
        }

        #region private void Save()
        /// <summary>
        /// 
        /// </summary>
        private bool Save()
        {
            bool success = _project.Save();
            if (!success)
            {
                MessageBox.Show("There was an error saving the project!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return success;
        }
        #endregion

        #region private void SaveAs(string newFileName)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="newFileName"></param>
        private bool SaveAs(string newFileName)
        {
            bool success = _project.SaveAs(newFileName);
            if (!success)
            {
                MessageBox.Show("There was an error saving the project!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return success;
        }
        #endregion

        #region private void openToolStripMenuItem_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the openToolStripMenuItem's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                InitProject(openFileDialog1.FileName);
            }
        }
        #endregion

        #region private void txtNamespace_TextChanged(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the txtNamespace's TextChanged event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void txtNamespace_TextChanged(object sender, EventArgs e)
        {
            txtNamespace.Text = txtNamespace.Text.Replace(" ", "");
            Properties.Settings.Default.OutputNamespace = txtNamespace.Text;
            Properties.Settings.Default.Save();
        }
        #endregion

        #region private void txtSavePath_TextChanged(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the txtSavePath's TextChanged event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void txtSavePath_TextChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.OutputPath = txtSavePath.Text;
            Properties.Settings.Default.Save();
        }
        #endregion

        #region private void newToolStripMenuItem_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the newToolStripMenuItem's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (CloseProject())
            {
                NewProject();
            }
        }
        #endregion

        #region private bool CloseProject()
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private bool CloseProject()
        {
            DialogResult dr = DialogResult.Yes;
            if (_project.IsModified)
            {
                dr = MessageBox.Show("The Project was modified. Do you want to save it ?", "Project modified", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (dr == DialogResult.Yes)
                {
                    return Save();
                }
            }
            return dr != DialogResult.Cancel;
        }
        #endregion

        #region private void NewProject()
        /// <summary>
        /// 
        /// </summary>
        private void NewProject()
        {
            InitProject(string.Empty);
        }
        #endregion

        #region private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the exitToolStripMenuItem's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (CloseProject())
            {
                Application.Exit();
            }
        }
        #endregion

        #region private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        /// <summary>
        /// This method is called when the MainForm's FormClosing event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="FormClosingEventArgs"/> of the event.</param>
        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = !CloseProject();
        }
        #endregion

        private void btnFindReplace_Click(object sender, EventArgs e)
        {
            //if (_findReplaceForm == null)
            {
                _findReplaceForm = new FindReplaceForm(_project.StringReplacement);
            }

            if (_findReplaceForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                _project.StringReplacement.Clear();
                _findReplaceForm.StringReplacementList.ToList().ForEach(item => _project.StringReplacement.Add(item));
                RefreshSelectedObjectsDataFromProject();
            }
        }

        private void projectOptionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_project.ShowOptions() == System.Windows.Forms.DialogResult.OK)
            { 

            }
        }

    }
}
